using IQIGame.Onigao.Framework;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;

namespace IQIGame.Onigao.Game
{
    /// <summary>
    /// 单次网络会话
    /// </summary>
    public class NetSession : ISession
    {
        /// <summary>
        /// channel
        /// </summary>
        private readonly INetworkChannel m_channel;
        /// <summary>
        /// SessionConnector
        /// </summary>
        public readonly INetSessionConnector SessionConnector;
        /// <summary>
        /// 是否关闭
        /// </summary>
        public bool IsClosed { get; private set; }
        /// <summary>
        /// 是否销毁
        /// </summary>
        private bool m_IsDisposed;
        /// <summary>
        /// 重连次数
        /// </summary>
        internal int ReconnectCount { get; private set; }
        /// <summary>
        /// 本次连接已读消息数量
        /// </summary>
        private volatile int m_readMsgCount;
        /// <summary>
        /// 当前网络连接id
        /// </summary>
        public string ConnectId { get; private set; }
        /// <summary>
        /// 重连的token
        /// </summary>
        public string ReconnectToken { get; private set; }
        /// <summary>
        /// 上一次连接已读消息数量保存,用于断线重连向服务器请求未读数据
        /// </summary>
        public int LastReadMsgCount { get; private set; }
        /// <summary>
        /// 网络消息序号
        /// </summary>
        private int m_msgOrder;
        /// <summary>
        /// 已发送消息缓存,用于断线重连
        /// </summary>
        private Queue<SendPackage> m_sendMsgCaches;
        public static readonly int CACHE_MESSAGE_COUNT = 10;
        /// <summary>
        /// 正在连接的地址
        /// </summary>
        private readonly string m_host;
        /// <summary>
        /// 正在连接的端口
        /// </summary>
        private readonly int m_port;
        /// <summary>
        /// 正在等待协议连接结果
        /// </summary>
        public bool WaitProtocolConnectResult;
        /// <summary>
        /// 是否正在重连中
        /// </summary>
        internal bool IsReconnecting;
        /// <summary>
        /// 等待协议连接中发送的包
        /// </summary>
        private Queue<SendPackage> m_waitProtocolReconnectCache;
        /// <summary>
        /// 最后一次心跳发送时间
        /// </summary>
        private float m_lastHeartBeatTime = 0;
        /// <summary>
        /// 服务器时区偏移
        /// </summary>
        public int ServerTimeRawOffset { get; private set; }
        /// <summary>
        /// 服务器时间
        /// </summary>
        private long m_serverTime;
        /// <summary>
        /// 记录同步服务器时间时的客户端时间, 用于计算最新的服务器时间
        /// </summary>
        private float m_lastSyncClientTime;

        private CNetHandler m_cNetHandler;

        internal NetSession(INetworkChannel channel, CNetHandler cNetHandler, INetSessionConnector sessionConnector, string host, int port)
        {
            m_sendMsgCaches = new Queue<SendPackage>();
            m_waitProtocolReconnectCache = new Queue<SendPackage>();
            this.m_channel = channel;
            this.m_port = port;
            this.m_host = host;
            this.SessionConnector = sessionConnector;
            this.m_cNetHandler = cNetHandler;
            FrameworkEntry.MonoDriver.updateEvent += OnUpdate;
        }

        /// <summary>
        /// 频道名称
        /// </summary>
        public string ChannelName
        {
            get
            {
                return m_channel.Name;
            }
        }

        private void OnUpdate(float deltaTime, float unscaledDeltaTime)
        {
            if (IsAvailable && m_lastHeartBeatTime != 0 && m_lastHeartBeatTime + 5 < Time.realtimeSinceStartup)
            {
                Debug.LogWarningFormat($"Send HeartBeat Timeout, Channel = {ChannelName}");
                //如果心跳包5秒没返回,就认为网络超时并断开重连
                Disconnect();
            }
        }

        /// <summary>
        /// 同步服务器时间
        /// </summary>
        /// <param name="time"></param>
        /// <param name="rawOffset"></param>
        public void SyncServerTime(long time, int? rawOffset = null)
        {
            if (rawOffset != null)
            {
                ServerTimeRawOffset = rawOffset.Value;
            }
            m_serverTime = time;
            m_lastSyncClientTime = Time.realtimeSinceStartup;
        }

        /// <summary>
        /// 获取当前的服务器时间戳（单位：毫秒）
        /// </summary>
        /// <returns></returns>
        public long GetServerTimestampMillis()
        {
            return m_serverTime + (long)((Time.realtimeSinceStartup - m_lastSyncClientTime) * 1000L);
        }

        /// <summary>
        /// 获取当前的服务器时间戳（单位：秒）
        /// </summary>
        /// <returns></returns>
        public int GetServerTimestamp()
        {
            return (int)(GetServerTimestampMillis() / 1000);
        }

        public DateTime GetServerDateTime()
        {
            //if (GameEntry.Base.DevMode)
            //{
            //    return DateTime.Now;
            //}
            //else
            {
                return DateTimeUtil.GetDateTime(GetServerTimestampMillis(), ServerTimeRawOffset);
            }
        }

        /// <summary>
        /// 发送心跳
        /// </summary>
        internal void SendHeartBeat()
        {
            if (IsAvailable && Connected)
            {
                SessionConnector.SendHeartBeat();
                m_lastHeartBeatTime = Time.realtimeSinceStartup;
            }
        }

        /// <summary>
        /// 收到心跳回包
        /// </summary>
        public void ReceiveHeartBeat()
        {
            m_lastHeartBeatTime = 0;
        }

        /// <summary>
        /// 缓存发送的消息包,最多10个
        /// </summary>
        /// <param name="package"></param>
        private void CachePackage(SendPackage package)
        {
            m_sendMsgCaches.Enqueue(package);
            package.CacheCount++;
            while (m_sendMsgCaches.Count > CACHE_MESSAGE_COUNT)
            {
                SendPackage detachPack = m_sendMsgCaches.Dequeue();
                detachPack.CacheCount--;
            }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="pack"></param>
        /// <returns></returns>
        public bool SendPack(SendPackage pack)
        {
            if (WaitProtocolConnectResult)
            {
                pack.CacheCount++;
                m_waitProtocolReconnectCache.Enqueue(pack);
                //if (isSendAllFailMsg)
                //{
                //Debug.LogWarning($"{ChannelName}SendAllFailMsg fail reason = WaitProtocolConnectResult");
                //}
                return false;
            }
            pack.Header.Order = ++m_msgOrder;

            CachePackage(pack);

            if (m_channel.IsAvailable && m_channel.Connected)
            {
                if (m_channel.Active)
                {
                    m_channel.Send(pack);
                    return true;
                }
                else
                {
                    Debug.LogWarning($"SendPack m_channel is not Active");
                }

            }
            //if (isSendAllFailMsg)
            //{
            //Debug.LogWarning($"{ChannelName} SendAllFailMsg fail reason = channel.IsAvailable && channel.Connected");
            //}
            return false;
        }

        /// <summary>
        /// 协议连接成功,发送之前服务器未收到的消息和协议连接中的消息
        /// </summary>
        /// <param name="lastMsgOrder">服务器最后收到的order, -1表示首次连接成功, 发送协议连接中的数据</param>
        /// <returns></returns>
        public bool SendAllCachePackage(int lastMsgOrder)
        {
            bool sendFlag = true;
            if (lastMsgOrder != -1)
            {
                Queue<SendPackage> tmpQueues = new Queue<SendPackage>(m_sendMsgCaches);
                m_sendMsgCaches.Clear();
                if (tmpQueues.Count > 0 && tmpQueues.Peek().Header.Order > lastMsgOrder + 1)
                {
                    //cache的缓存过小, 不能与服务器最后收到的order序号接上
                    Debug.LogWarning($"{ChannelName} SendAllFailPackage sendMsgCaches is small");
                    return false;
                }

                foreach (SendPackage pack in tmpQueues)
                {
                    if (pack.Header.Order > lastMsgOrder)
                    {
                        sendFlag &= SendPack(pack);
                    }
                    pack.CacheCount--;
                }
            }

            foreach (SendPackage pack in m_waitProtocolReconnectCache)
            {
                sendFlag &= SendPack(pack);
                pack.CacheCount--;
            }
            m_waitProtocolReconnectCache.Clear();

            return sendFlag;
        }

        /// <summary>
        /// 协议连接失败
        /// </summary>
        public void OnProtocolConnectFail()
        {
            Debug.LogWarning($"Session OnProtocolConnectFail channel = {ChannelName}");
            ReconnectToken = null;
            ConnectId = null;
            Close();
        }

        /// <summary>
        /// 协议连接或者重连成功后调用
        /// </summary>
        /// <param name="token"></param>
        /// <param name="connectId"></param>
        /// <param name="lastMsgOrder"> -1表示首次连接成功, 发送协议连接中的数据</param>
        /// <exception cref="Exception"></exception>
        public void OnProtocolConnectSuccess(string reconnectToken, string connectId, int lastMsgOrder = -1)
        {
            if (reconnectToken == null)
            {
                throw new Exception($"{ChannelName} OnProtocolConnectSuccess can not set token = null");
            }
            //if (!IsAvailable || !Connected)
            //{
            //    Debug.LogWarningFormat("OnProtocolConnectSuccess but the session is closed, channel = {0}", ChannelName);
            //    return;
            //}
            ReconnectCount = 0;
            ReconnectToken = reconnectToken;
            ConnectId = connectId;
            Debug.LogWarning($"Session OnProtocolConnectSuccess channel = {ChannelName}");
            bool sendResult = SendAllCachePackage(lastMsgOrder);
            if (!sendResult)
            {
                Debug.LogWarning($"SendAllCachePackage fail, channel = {ChannelName}");
                //发送缓存数据包失败
                Close();
            }
        }

        public bool CanReconnect()
        {
            if (IsAvailable)
            {
                return false;
            }
            if (IsClosed)
            {
                return false;
            }
            if (ReconnectToken == null || ConnectId == null)
            {
                return false;
            }
            if (ReconnectCount > 10)
            {
                //重连次数过多
                //需要设置一个重连次数上限, 当ReconnectToken不为空, 服务器网络是通的, 但是进行协议连接发消息就断开连接, 会无限重连
                return false;
            }
            return true;
        }

        /// <summary>
        /// session断线重连
        /// </summary>
        /// <returns>网络是否连接成功, 不代表协议重连成功</returns>
        public async Task<bool> Reconnect()
        {
            if (IsReconnecting)
            {
                return false;
            }
            if (!CanReconnect())
            {
                return false;
            }
            if (ReconnectCount++ == 0)
            {
                LastReadMsgCount = m_readMsgCount;//第一次网络断开记录LastReadMsgCount才是最准确的, 这里在第一次重连记录也是可以的
                Debug.LogWarning($"Record LastReadMsgCount = {LastReadMsgCount}, Channel = {ChannelName}");
            }
            m_readMsgCount = 0;//每次重连都需要重置,虽然是多线程修改, 但是业务上不会出现并发问题. session断开后m_readMsgCount就不会更改
            m_lastHeartBeatTime = 0;//每次重连都需要重置
            Debug.LogWarning($"Reconnect Channel = {ChannelName}");
            IsReconnecting = true;
            var netResult = await NetManager.Instance.ConnectToChannel(m_channel, m_host, m_port);
            IsReconnecting = false;
            Debug.LogWarning($"EndReconnect Channel = {ChannelName}, NetResult = {netResult}");
            m_msgOrder = 0;//每次连接都需要重置, 需要网络连接后重置, 这里网络连接失败也可以重置
            if (netResult)
            {
                //网络重新连接成功后进行协议重连
                _ = SessionConnector.ReconnectServer(this);
            }
            return netResult;
        }

        /// <summary>
        /// 断开Session
        /// </summary>
        public void Disconnect()
        {
            m_channel.Close();
        }

        /// <summary>
        /// 关闭Session
        /// </summary>
        public void Close(bool active = false)
        {
            if (IsClosed)
            {
                return;
            }
            CleanMsgAwait();
            IsClosed = true;
            m_channel.Close();
            SessionConnector.OnSessionClosed(this, active);
        }

        /// <summary>
        /// 清理MsgAsyncAwaiter
        /// </summary>
        public void CleanMsgAwait()
        {
            (int, int) msgIdRange = NetManager.Instance.GetChannelMsgIdRange(ChannelName);
            MsgAsyncAwaiterAll.ClearMsgAwait(msgIdRange.Item1, msgIdRange.Item2);
        }

        /// <summary>
        /// session是否可用
        /// </summary>
        public bool IsAvailable
        {
            get
            {
                return !IsClosed && m_channel.IsAvailable;
            }
        }

        /// <summary>
        /// 获取是否已连接。
        /// </summary>
        public bool Connected
        {
            get
            {
                return !IsClosed && m_channel.Connected;
            }
        }

        public IExecutor GetExecutor()
        {
            return DefaultSessionExecutor.Instance;
        }

        /// <summary>
        /// 这里是网络线程调用进来执行
        /// </summary>
        /// <param name="package"></param>
        /// <returns></returns>
        public bool ProcessReceivePackage(ReceivePackage package)
        {
            if (!IsAvailable)
            {
                return false;
            }
            m_readMsgCount++;
            return m_cNetHandler.ProcessPackage(this, package);
        }

        internal void Dispose()
        {
            if (m_IsDisposed)
            {
                return;
            }
            m_IsDisposed = true;
            FrameworkEntry.MonoDriver.updateEvent -= OnUpdate;
            foreach (SendPackage pack in m_sendMsgCaches)
            {
                pack.CacheCount--;
            }
            m_sendMsgCaches.Clear();
            m_sendMsgCaches = null;

            foreach (SendPackage pack in m_waitProtocolReconnectCache)
            {
                pack.CacheCount--;
            }
            m_waitProtocolReconnectCache.Clear();
            m_waitProtocolReconnectCache = null;

            ReconnectToken = null;
            ConnectId = null;
            m_cNetHandler = null;
        }
    }


}
